/******************************************************************************
 * FILE: omp_hello.c
 * DESCRIPTION:
 *   OpenMP Example - Hello World - C/C++ Version
 *   In this simple example, the master thread forks a parallel region.
 *   All threads in the team obtain their unique thread number and print it.
 *   The master thread only prints the total number of threads.  Two OpenMP
 *   library routines are used to obtain the number of threads and each
 *   thread's number.
 * AUTHOR: Blaise Barney  5/99
 * LAST REVISED: 04/06/05
 ******************************************************************************/
#include <omp.h>
#include <stdio.h>
#include <stdlib.h>
static int N;
static int NBPOS;
static int HOLEPOS;




typedef struct {	
	_Bool *gameHoles;
	int	nombreDeBille;
}GAMETABLE;




int calculateNbpos(int n)
{
	int nbPos = ((n+1)*n)/2;
	return nbPos;
}




void initN ()
{
	printf("Entrer le nombre de lignes de votre solitaire :");
	scanf("%d",&N);
	NBPOS = calculateNbpos(N);
	printf("\nNombre de ligne du plateau : %d  :\n",N);
	printf("Nombre d'emplacement de la plateau : %d  :\n\n",NBPOS);
}




void initHolePos ()
{
	printf("Entrer la position du trou au départ (1 à n) :");
	scanf("%d",&HOLEPOS);	
}




GAMETABLE* initGameTable()
{
	GAMETABLE *gametable;
	if ((gametable = (GAMETABLE *)malloc(sizeof(GAMETABLE))) != NULL) {
		if ((gametable->gameHoles = (_Bool *)malloc(NBPOS*sizeof(_Bool))) != NULL) {
			for (int i = 0; i < NBPOS; i++) {
				if (i == HOLEPOS-1) {
					gametable->gameHoles[i] = 0x0;
				}else {
					gametable->gameHoles[i] = 0x1;
				}				
			}
		}
	}	
	gametable->nombreDeBille = NBPOS-1;
	return gametable;
}




void printGameTable(GAMETABLE* gametable)
{
	printf("Game table");
	for (int j=0; j<NBPOS; j++) 
	{
		printf(" %d -", gametable->gameHoles[j]);
	}
	printf("\n");
}




void findMoves2(GAMETABLE* gametable)
{
	int countElemPerLine=0;
	int currentLine = 1;
	
	
	//printf("Nbre de bille %d \n",gametable->nombreDeBille);
	if (gametable->nombreDeBille ==1) {
		printGameTable(gametable);
	}
	//printGameTable(gametable);

	
	
	// Test toutes les positions du Plateau pour trouver tous les mouvements possibles et exécuter les plateaux ayant une bille en moins (utilise un for séquentiel pour parcourir en profondeur)
	for (int i=0;  i< (NBPOS); i++) 
	{
		countElemPerLine +=1;
		
		if (gametable->gameHoles[i] == 0x0) // Si il y a un trou on essaie tous les mouvements pour le combler
		{ 			
				#pragma omp parallel num_threads(6)  shared(i,currentLine,countElemPerLine)
				{
					
					
					#pragma omp sections 
					{
						
						
						#pragma omp section 
						{
							//Obtenir le prochain plateau avec un mouvement horizontal depuis la gauche
							if (gametable->gameHoles[i-1] == 0x1 && gametable->gameHoles[i-2] == 0x1 && ((countElemPerLine - 2) > 0)) 
							{
								GAMETABLE *gametableMhg;
								if ((gametableMhg = (GAMETABLE *)malloc(sizeof(GAMETABLE))) != NULL) {
									if ((gametableMhg->gameHoles = (_Bool *)malloc(NBPOS * sizeof(_Bool))) != NULL) {
										gametableMhg->nombreDeBille = gametable->nombreDeBille - 1;

									
										#pragma omp parallel num_threads(6) 
										{
											int j;
											
											#pragma omp for
										
											for (j=0; j<NBPOS; j++) 
											{
												gametableMhg->gameHoles[j] = gametable->gameHoles[j];
											}									
										}
									
										gametableMhg->gameHoles[i] = 0x1;
										gametableMhg->gameHoles[i-1] = 0x0;
										gametableMhg->gameHoles[i-2] = 0x0;
										findMoves2(gametableMhg);
									}
								}
							}
						}	
						
						
						
					
						#pragma omp section
						{
							//Obtenir le prochain plateau avec un mouvement horizontal depuis la doite
							if (gametable->gameHoles[i+1] == 0x1 && gametable->gameHoles[i+2] == 0x1 && (countElemPerLine + 2) < (currentLine + 1)) 
							{
								GAMETABLE *gametableMhd;
								if ((gametableMhd = (GAMETABLE *)malloc(sizeof(GAMETABLE))) != NULL) {
									if ((gametableMhd->gameHoles = (_Bool *)malloc(NBPOS*sizeof(_Bool))) != NULL) {
										gametableMhd->nombreDeBille = gametable->nombreDeBille - 1;

										
										#pragma omp parallel num_threads(6) 
										{
											int j;
											
											#pragma omp for
											
											for (j=0; j<NBPOS; j++) 
											{
												gametableMhd->gameHoles[j] = gametable->gameHoles[j];	
											}										
										}

										gametableMhd->gameHoles[i] = 0x1;
										gametableMhd->gameHoles[i+1] = 0x0;
										gametableMhd->gameHoles[i+2] = 0x0;
										findMoves2(gametableMhd);
									}	
								}
							}	
						}
						
						
						
					
						#pragma omp section
						{
							//Obtenir le prochain plateau avec un mouvement oblique depuis le coin haut gauche
							if (gametable->gameHoles[i-currentLine] == 0x1 && gametable->gameHoles[((i-currentLine)-currentLine+1)] == 0x1 && (countElemPerLine - 2) > 0) 
							{
								GAMETABLE *gametableMohg;
								if ((gametableMohg = (GAMETABLE *)malloc(sizeof(GAMETABLE))) != NULL) {
									if ((gametableMohg->gameHoles = (_Bool *)malloc(NBPOS*sizeof(_Bool))) != NULL) {
										gametableMohg->nombreDeBille = gametable->nombreDeBille - 1;
										
										
										#pragma omp parallel num_threads(6) 
										{
											int j;
											
											#pragma omp for
											
											for (j=0; j<NBPOS; j++) 
											{
												gametableMohg->gameHoles[j] = gametable->gameHoles[j];
											}								
										}
										
										gametableMohg->gameHoles[i] = 0x1;
										gametableMohg->gameHoles[(i-currentLine)] = 0x0;
										gametableMohg->gameHoles[((i-currentLine)-currentLine+1)] = 0x0;
										findMoves2(gametableMohg);
									}
								}
							}	
						}
						
						

					
						#pragma omp section
						{	
							//Obtenir le prochain plateau avec un mouvement oblique depuis le coin bas droite
							if (gametable->gameHoles[i+currentLine+1] == 0x1 && gametable->gameHoles[(i+currentLine+1)+(currentLine+2)] == 0x1 && currentLine < (N-1)) 
							{
								GAMETABLE *gametableMobd;
								if ((gametableMobd = (GAMETABLE *)malloc(sizeof(GAMETABLE))) != NULL) {
									if ((gametableMobd->gameHoles = (_Bool *)malloc(NBPOS*sizeof(_Bool))) != NULL) {
										gametableMobd->nombreDeBille = gametable->nombreDeBille - 1;
										

										#pragma omp parallel num_threads(6) 
										{
											int j;
											
											#pragma omp for
											
											for (j=0; j<NBPOS; j++) 
											{
												gametableMobd->gameHoles[j] = gametable->gameHoles[j];
											}											
										}
										
										gametableMobd->gameHoles[i] = 0x1;
										gametableMobd->gameHoles[(i+currentLine+1)] = 0x0;
										gametableMobd->gameHoles[(i+currentLine+1)+(currentLine+2)] = 0x0;
										findMoves2(gametableMobd);
									}
								}
							}
						}
						

						
					
						#pragma omp section
						{				
							//Obtenir le prochain plateau avec un mouvement oblique depuis le coin bas gauche
							if (gametable->gameHoles[i+currentLine] == 0x1 && gametable->gameHoles[(i+currentLine)+(currentLine+1)] == 0x1 && currentLine < (N-1)) 
							{
								GAMETABLE *gametableMobg;
								if ((gametableMobg = (GAMETABLE *)malloc(sizeof(GAMETABLE))) != NULL) {
									if ((gametableMobg->gameHoles = (_Bool *)malloc(NBPOS*sizeof(_Bool))) != NULL) {
										gametableMobg->nombreDeBille = gametable->nombreDeBille - 1;
										
										
										#pragma omp parallel num_threads(6) 
										{
											int j;
											
											#pragma omp for
											
											for (j=0; j<NBPOS; j++) 
											{
												gametableMobg->gameHoles[j] = gametable->gameHoles[j];
											}								
										}
										
										gametableMobg->gameHoles[i] = 0x1;
										gametableMobg->gameHoles[(i+currentLine)] = 0x0;
										gametableMobg->gameHoles[(i+currentLine)+(currentLine+1)] = 0x0;
										findMoves2(gametableMobg);
									}
								}
							}	
						}
					
						
						
						
						#pragma omp section
						{
							//Obtenir le prochain plateau avec un mouvement oblique depuis le coin haut droite
							if (gametable->gameHoles[(i-currentLine+1)] == 0x1 && gametable->gameHoles[((i-currentLine+1)-currentLine+2)] == 0x1 && (countElemPerLine + 2) < (currentLine + 1)) 
							{
								GAMETABLE *gametableMohd;
								if ((gametableMohd = (GAMETABLE *)malloc(sizeof(GAMETABLE))) != NULL) {
									if ((gametableMohd->gameHoles = (_Bool *)malloc(NBPOS*sizeof(_Bool))) != NULL) {
										gametableMohd->nombreDeBille = gametable->nombreDeBille - 1;
										
										
										#pragma omp parallel num_threads(6) 
										{
											int j;
											
											#pragma omp for
											
											for (j=0; j<NBPOS; j++) 
											{
												gametableMohd->gameHoles[j] = gametable->gameHoles[j];
											}										
										}	
										
										gametableMohd->gameHoles[i] = 0x1;
										gametableMohd->gameHoles[(i-currentLine+1)] = 0x0;
										gametableMohd->gameHoles[((i-currentLine+1)-currentLine+2)] = 0x0;
										findMoves2(gametableMohd);
									}
								}
							}
							
						}
					}
				}
			}
		
			if (countElemPerLine == currentLine) {
			countElemPerLine = 0;
			currentLine += 1;
		}
	}
	
	// TO DO
	// if (!gametable->IsInDispertionTable){ free(gameTable)}
	free(gametable);
	
}	
		
















void findMoves1(GAMETABLE* gametable)
{
	int d=0;
	int g=0;
	for (int m=1; m<(N+1); m++) {
		d=d+m;
		g=d;
		for (int i=1; i<(N+1); i++) {
			g=g+i-1;
			if (gametable->gameHoles[g] == 0x1 && gametable->gameHoles[g+i] == 0x1 && gametable->gameHoles[g+2*i+1] == 0x0 ){
				
				// a move has been found
				//TO DO créer un nouveau plateau avec la bille en moins et le nombre de billes -1 
				// et soit l'ajouter à la liste finale car reste qu une bille, soit l'ajouter à la liste des tache et l
			}
		}
	}
}








void testParallele()
{
	int nthreads, tid;
	/* Fork a team of threads giving them their own copies of variables */
#pragma omp parallel  private(nthreads, tid)
	{
		
		/* Obtain thread number */
		tid = omp_get_thread_num();
		printf("Hello World from thread = %d\n", tid);
		
		/* Only master thread does this */
		if (tid == 0) 
		{
			nthreads = omp_get_num_threads();
			printf("Number of threads = %d\n", nthreads);
		}
	}  /* All threads join master thread and disband */
}

	








int main (int argc, char *argv[]) 
{

	
	
	initN();
	initHolePos();
	//N = 5;
	//calculateNbpos(N);
	//HOLEPOS =5;
	
	findMoves2(initGameTable());
	
	

	
}





